1 /**
2  * Dynamic Array
3  * Copyright: © 2015 Economic Modeling Specialists, Intl.
4  * Authors: Brian Schott
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6  */
7 
8 module containers.dynamicarray;
9 
10 private import core.lifetime : move, moveEmplace, copyEmplace, emplace;
11 private import std.traits : isCopyable;
12 private import containers.internal.node : shouldAddGCRange;
13 private import std.experimental.allocator.mallocator : Mallocator;
14 
15 /**
16  * Array that is able to grow itself when items are appended to it. Uses
17  * malloc/free/realloc to manage its storage.
18  *
19  * Params:
20  *     T = the array element type
21  *     Allocator = the allocator to use. Defaults to `Mallocator`.
22  *     supportGC = true if the container should support holding references to
23  *         GC-allocated memory.
24  */
25 struct DynamicArray(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange!T)
26 {
27 	this(this) @disable;
28 
29 	private import std.experimental.allocator.common : stateSize;
30 
31 	static if (is(typeof((T[] a, const T[] b) => a[0 .. b.length] = b[0 .. $])))
32 	{
33 		/// Either `const(T)` or `T`.
34 		alias AppendT = const(T);
35 
36 		/// Either `const(typeof(this))` or `typeof(this)`.
37 		alias AppendTypeOfThis = const(typeof(this));
38 	}
39 	else
40 	{
41 		alias AppendT = T;
42 		alias AppendTypeOfThis = typeof(this);
43 	}
44 
45 	static if (stateSize!Allocator != 0)
46 	{
47 		/// No default construction if an allocator must be provided.
48 		this() @disable;
49 
50 		/**
51 		 * Use the given `allocator` for allocations.
52 		 */
53 		this(Allocator allocator)
54 		in
55 		{
56 			assert(allocator !is null, "Allocator must not be null");
57 		}
58 		do
59 		{
60 			this.allocator = allocator;
61 		}
62 	}
63 
64 	~this()
65 	{
66 		import std.experimental.allocator.mallocator : Mallocator;
67 		import containers.internal.node : shouldAddGCRange;
68 
69 		if (arr is null)
70 			return;
71 
72 		static if ((is(T == struct) || is(T == union))
73 			&& __traits(hasMember, T, "__xdtor"))
74 		{
75 			foreach (ref item; arr[0 .. l])
76 			{
77 				item.__xdtor();
78 			}
79 		}
80 		static if (useGC)
81 		{
82 			import core.memory : GC;
83 			GC.removeRange(arr.ptr);
84 		}
85 		allocator.deallocate(arr);
86 	}
87 
88 	/// Slice operator overload
89 	pragma(inline, true)
90 	auto opSlice(this This)() @nogc
91 	{
92 		return opSlice!(This)(0, l);
93 	}
94 
95 	/// ditto
96 	pragma(inline, true)
97 	auto opSlice(this This)(size_t a, size_t b) @nogc
98 	{
99 		alias ET = ContainerElementType!(This, T);
100 		return cast(ET[]) arr[a .. b];
101 	}
102 
103 	/// Index operator overload
104 	pragma(inline, true)
105 	ref auto opIndex(this This)(size_t i) @nogc
106 	{
107 		return opSlice!(This)(i, i + 1)[0];
108 	}
109 
110 	/**
111 	 * Inserts the given value into the end of the array.
112 	 */
113 	void insertBack(T value)
114 	{
115 		import std.experimental.allocator.mallocator : Mallocator;
116 		import containers.internal.node : shouldAddGCRange;
117 
118 		if (arr.length == 0)
119 		{
120 			arr = cast(typeof(arr)) allocator.allocate(T.sizeof * 4);
121 			static if (useGC)
122 			{
123 				import core.memory: GC;
124 				GC.addRange(arr.ptr, arr.length * T.sizeof);
125 			}
126 		}
127 		else if (l >= arr.length)
128 		{
129 			immutable size_t c = arr.length > 512 ? arr.length + 1024 : arr.length << 1;
130 			static if (useGC)
131 				void* oldPtr = arr.ptr;
132 			void[] a = cast(void[]) arr;
133 			import std.experimental.allocator.common : reallocate;
134 			allocator.reallocate(a, c * T.sizeof);
135 			arr = cast(typeof(arr)) a;
136 			static if (useGC)
137 			{
138 				import core.memory: GC;
139 				GC.removeRange(oldPtr);
140 				GC.addRange(arr.ptr, arr.length * T.sizeof);
141 			}
142 		}
143 		moveEmplace(*cast(ContainerStorageType!T*)&value, arr[l++]);
144 	}
145 
146 	/// ditto
147 	alias insert = insertBack;
148 
149 	/// ditto
150 	alias insertAnywhere = insertBack;
151 
152 	/// ditto
153 	alias put = insertBack;
154 
155 	/**
156 	 * ~= operator overload
157 	 */
158 	scope ref typeof(this) opOpAssign(string op)(T value) if (op == "~")
159 	{
160 		insert(value);
161 		return this;
162 	}
163 
164 	/**
165 	* ~= operator overload for an array of items
166 	*/
167 	scope ref typeof(this) opOpAssign(string op, bool checkForOverlap = true)(AppendT[] rhs)
168 		if (op == "~" && !is(T == AppendT[]))
169 	{
170 		// Disabling checkForOverlap when this function is called from opBinary!"~"
171 		// is not just for efficiency, but to avoid circular function calls that
172 		// would prevent inference of @nogc, etc.
173 		static if (checkForOverlap)
174 		if ((() @trusted => arr.ptr <= rhs.ptr && arr.ptr + arr.length > rhs.ptr)())
175 		{
176 			// Special case where rhs is a slice of this array.
177 			this = this ~ rhs;
178 			return this;
179 		}
180 		reserve(l + rhs.length);
181 		import std.traits: hasElaborateAssign, hasElaborateDestructor;
182 		static if (is(T == struct) && (hasElaborateAssign!T || hasElaborateDestructor!T))
183 		{
184 			foreach (ref value; rhs)
185 				copyEmplace(value, arr[l++]);
186 		}
187 		else
188 		{
189 			arr[l .. l + rhs.length] = rhs[0 .. rhs.length];
190 			l += rhs.length;
191 		}
192 		return this;
193 	}
194 
195 	/// ditto
196 	scope ref typeof(this) opOpAssign(string op)(ref AppendTypeOfThis rhs)
197 		if (op == "~")
198 	{
199 		return this ~= rhs.arr[0 .. rhs.l];
200 	}
201 
202 	/**
203 	 * ~ operator overload
204 	 */
205 	typeof(this) opBinary(string op)(ref AppendTypeOfThis other) if (op == "~")
206 	{
207 		typeof(this) ret;
208 		ret.reserve(l + other.l);
209 		ret.opOpAssign!("~", false)(arr[0 .. l]);
210 		ret.opOpAssign!("~", false)(other.arr[0 .. other.l]);
211 		return ret;
212 	}
213 
214 	/// ditto
215 	typeof(this) opBinary(string op)(AppendT[] values) if (op == "~")
216 	{
217 		typeof(this) ret;
218 		ret.reserve(l + values.length);
219 		ret.opOpAssign!("~", false)(arr[0 .. l]);
220 		ret.opOpAssign!("~", false)(values);
221 		return ret;
222 	}
223 
224 	/**
225 	 * Ensures sufficient capacity to accommodate `n` elements.
226 	 */
227 	void reserve(size_t n)
228 	{
229 		if (arr.length >= n)
230 			return;
231 		if (arr.ptr is null)
232 		{
233 			size_t c = 4;
234 			if (c < n)
235 				c = n;
236 			arr = cast(typeof(arr)) allocator.allocate(T.sizeof * c);
237 			static if (useGC)
238 			{
239 				import core.memory: GC;
240 				GC.addRange(arr.ptr, arr.length * T.sizeof);
241 			}
242 		}
243 		else
244 		{
245 			size_t c = arr.length > 512 ? arr.length + 1024 : arr.length << 1;
246 			if (c < n)
247 				c = n;
248 			static if (useGC)
249 				void* oldPtr = arr.ptr;
250 			void[] a = cast(void[]) arr;
251 			import std.experimental.allocator.common : reallocate;
252 			allocator.reallocate(a, c * T.sizeof);
253 			arr = cast(typeof(arr)) a;
254 			static if (useGC)
255 			{
256 				import core.memory: GC;
257 				GC.removeRange(oldPtr);
258 				GC.addRange(arr.ptr, arr.length * T.sizeof);
259 			}
260 		}
261 	}
262 
263 	/**
264 	 * Change the array length.
265 	 * When growing, initialize new elements to the default value.
266 	 */
267 	static if (is(typeof({static T value;}))) // default construction is allowed
268 	void resize(size_t n)
269 	{
270 		import std.traits: hasElaborateAssign, hasElaborateDestructor;
271 		auto toFill = resizeStorage(n);
272 		static if (is(T == struct) && hasElaborateDestructor!T)
273 		{
274 			foreach (ref target; toFill)
275 				emplace(&target);
276 		}
277 		else
278 			toFill[] = T.init;
279 	}
280 
281 	/**
282 	 * Change the array length.
283 	 * When growing, initialize new elements to the given value.
284 	 */
285 	static if (isCopyable!T)
286 	void resize(size_t n, T value)
287 	{
288 		import std.traits: hasElaborateAssign, hasElaborateDestructor;
289 		auto toFill = resizeStorage(n);
290 		static if (is(T == struct) && (hasElaborateAssign!T || hasElaborateDestructor!T))
291 		{
292 			foreach (ref target; toFill)
293 				copyEmplace(value, target);
294 		}
295 		else
296 			toFill[] = value;
297 	}
298 
299 	// Resizes storage only, and returns slice of new memory to fill.
300 	private ContainerStorageType!T[] resizeStorage(size_t n)
301 	{
302 		ContainerStorageType!T[] toFill = null;
303 
304 		if (arr.length < n)
305 			reserve(n);
306 
307 		if (l < n) // Growing?
308 		{
309 			toFill = arr[l..n];
310 		}
311 		else
312 		{
313 			static if ((is(T == struct) || is(T == union))
314 				&& __traits(hasMember, T, "__xdtor"))
315 			{
316 				foreach (i; n..l)
317 					arr[i].__xdtor();
318 			}
319 		}
320 
321 		l = n;
322 		return toFill;
323 	}
324 
325 	/**
326 	 * Remove the item at the given index from the array.
327 	 */
328 	void remove(const size_t i)
329 	{
330 		if (i < this.l)
331 		{
332 			auto next = i + 1;
333 			while (next < this.l)
334 			{
335 				move(arr[next], arr[next - 1]);
336 				++next;
337 			}
338 
339 			--l;
340 			static if ((is(T == struct) || is(T == union))
341 				&& __traits(hasMember, T, "__xdtor"))
342 			{
343 				arr[l].__xdtor();
344 			}
345 		}
346 		else
347 		{
348 			import core.exception : RangeError;
349 			throw new RangeError("Out of range index used to remove element");
350 		}
351 	}
352 
353 	/**
354 	 * Removes the last element from the array.
355 	 */
356 	void removeBack()
357 	{
358 		this.remove(this.length - 1);
359 	}
360 
361 	/// Index assignment support
362 	void opIndexAssign(T value, size_t i) @nogc
363 	{
364 		arr[i] = move(*cast(ContainerStorageType!T*)&value);
365 	}
366 
367 	/// Slice assignment support
368 	static if (isCopyable!T)
369 	void opSliceAssign(T value) @nogc
370 	{
371 		arr[0 .. l] = value;
372 	}
373 
374 	/// ditto
375 	static if (isCopyable!T)
376 	void opSliceAssign(T value, size_t i, size_t j) @nogc
377 	{
378 		arr[i .. j] = value;
379 	}
380 
381 	/// ditto
382 	static if (isCopyable!T)
383 	void opSliceAssign(T[] values) @nogc
384 	{
385 		arr[0 .. l] = values[];
386 	}
387 
388 	/// ditto
389 	static if (isCopyable!T)
390 	void opSliceAssign(T[] values, size_t i, size_t j) @nogc
391 	{
392 		arr[i .. j] = values[];
393 	}
394 
395 	/// Returns: the number of items in the array
396 	size_t length() const nothrow pure @property @safe @nogc { return l; }
397 
398 	/// Ditto
399 	alias opDollar = length;
400 
401 	/// Returns: whether or not the DynamicArray is empty.
402 	bool empty() const nothrow pure @property @safe @nogc { return l == 0; }
403 
404 	/**
405 	 * Returns: a slice to the underlying array.
406 	 *
407 	 * As the memory of the array may be freed, access to this array is
408 	 * highly unsafe.
409 	 */
410 	auto ptr(this This)() @nogc @property
411 	{
412 		alias ET = ContainerElementType!(This, T);
413 		return cast(ET*) arr.ptr;
414 	}
415 
416 	/// Returns: the front element of the DynamicArray.
417 	auto ref T front() pure @property
418 	{
419 		return arr[0];
420 	}
421 
422 	/// Returns: the back element of the DynamicArray.
423 	auto ref T back() pure @property
424 	{
425 		return arr[l - 1];
426 	}
427 
428 private:
429 
430 	import containers.internal.storage_type : ContainerStorageType;
431 	import containers.internal.element_type : ContainerElementType;
432 	import containers.internal.mixins : AllocatorState;
433 
434 	enum bool useGC = supportGC && shouldAddGCRange!T;
435 	mixin AllocatorState!Allocator;
436 	ContainerStorageType!(T)[] arr;
437 	size_t l;
438 }
439 
440 version(emsi_containers_unittest) unittest
441 {
442 	import std.algorithm : equal;
443 	import std.range : iota;
444 	DynamicArray!int ints;
445 	assert(ints.empty);
446 	foreach (i; 0 .. 100)
447 	{
448 		ints.insert(i);
449 		assert(ints.front == 0);
450 		assert(ints.back == i);
451 	}
452 
453 	assert (equal(ints[], iota(100)));
454 	assert (ints.length == 100);
455 	ints[0] = 100;
456 	assert (ints[0] == 100);
457 	ints[0 .. 5] = 20;
458 	foreach (i; ints[0 .. 5])
459 		assert (i == 20);
460 	ints[] = 432;
461 	foreach (i; ints[])
462 		assert (i == 432);
463 
464 	auto arr = ints.ptr;
465 	arr[0] = 1337;
466 	assert(arr[0] == 1337);
467 	assert(ints[0] == 1337);
468 }
469 
470 version(emsi_containers_unittest)
471 {
472 	class Cls
473 	{
474 		int* a;
475 
476 		this(int* a)
477 		{
478 			this.a = a;
479 		}
480 
481 		~this()
482 		{
483 			++(*a);
484 		}
485 	}
486 }
487 
488 version(emsi_containers_unittest) unittest
489 {
490 	int* a = new int;
491 	{
492 		DynamicArray!(Cls) arr;
493 		arr.insert(new Cls(a));
494 	}
495 	assert(*a == 0); // Destructor not called.
496 }
497 
498 version(emsi_containers_unittest) unittest
499 {
500 	import std.exception : assertThrown;
501 	import core.exception : RangeError;
502 	DynamicArray!int empty;
503 	assertThrown!RangeError(empty.remove(1337));
504 	assert(empty.length == 0);
505 
506 	DynamicArray!int one;
507 	one.insert(0);
508 	assert(one.length == 1);
509 	assertThrown!RangeError(one.remove(1337));
510 	assert(one.length == 1);
511 	one.remove(0);
512 	assert(one.length == 0);
513 
514 	DynamicArray!int two;
515 	two.insert(0);
516 	two.insert(1);
517 	assert(two.length == 2);
518 	assertThrown!RangeError(two.remove(1337));
519 	assert(two.length == 2);
520 	two.remove(0);
521 	assert(two.length == 1);
522 	assert(two[0] == 1);
523 	two.remove(0);
524 	assert(two.length == 0);
525 
526 	two.insert(0);
527 	two.insert(1);
528 	assert(two.length == 2);
529 
530 	two.remove(1);
531 	assert(two.length == 1);
532 	assert(two[0] == 0);
533 	assertThrown!RangeError(two.remove(1));
534 	assert(two.length == 1);
535 	assert(two[0] == 0);
536 	two.remove(0);
537 	assert(two.length == 0);
538 }
539 
540 version(emsi_containers_unittest) unittest
541 {
542 	int* a = new int;
543 	DynamicArray!(Cls, Mallocator, true) arr;
544 	arr.insert(new Cls(a));
545 
546 	arr.remove(0);
547 	assert(*a == 0); // Destructor not called.
548 }
549 
550 version(emsi_containers_unittest) unittest
551 {
552 	DynamicArray!(int*, Mallocator, true) arr;
553 
554 	foreach (i; 0 .. 4)
555 		arr.insert(new int(i));
556 
557 	assert (arr.length == 4);
558 
559 	int*[] slice = arr[1 .. $ - 1];
560 	assert (slice.length == 2);
561 	assert (*slice[0] == 1);
562 	assert (*slice[1] == 2);
563 }
564 
565 version(emsi_containers_unittest) unittest
566 {
567 	import std.format : format;
568 
569 	DynamicArray!int arr;
570 	foreach (int i; 0 .. 10)
571 		arr ~= i;
572 	assert(arr.length == 10, "arr.length = %d".format(arr.length));
573 
574 	auto arr2 = arr ~ arr;
575 	assert(arr2.length == 20);
576 	auto arr3 = arr2 ~ [100, 99, 98];
577 	assert(arr3.length == 23);
578 
579 	while(!arr3.empty)
580 		arr3.removeBack();
581 	assert(arr3.empty);
582 
583 }
584 
585 version(emsi_containers_unittest) @system unittest
586 {
587 	DynamicArray!int a;
588 	a.reserve(1000);
589 	assert(a.length == 0);
590 	assert(a.empty);
591 	assert(a.arr.length >= 1000);
592 	int* p = a[].ptr;
593 	foreach (i; 0 .. 1000)
594 	{
595 		a.insert(i);
596 	}
597 	assert(p is a[].ptr);
598 }
599 
600 version(emsi_containers_unittest) unittest
601 {
602 	// Ensure that Array.insert doesn't call the destructor for
603 	// a struct whose state is uninitialized memory.
604 	static struct S
605 	{
606 		int* a;
607 		~this() @nogc nothrow
608 		{
609 			if (a !is null)
610 				++(*a);
611 		}
612 	}
613 	int* a = new int;
614 	{
615 		DynamicArray!S arr;
616 		// This next line may segfault if destructors are called
617 		// on structs in invalid states.
618 		arr.insert(S(a));
619 	}
620 	assert(*a == 1);
621 }
622 
623 version(emsi_containers_unittest) @nogc unittest
624 {
625 	struct HStorage
626 	{
627 		import containers.dynamicarray: DynamicArray;
628 		DynamicArray!int storage;
629 	}
630 	auto hs = HStorage();
631 }
632 
633 version(emsi_containers_unittest) @nogc unittest
634 {
635 	DynamicArray!char a;
636 	const DynamicArray!char b = a ~ "def";
637 	a ~= "abc";
638 	a ~= b;
639 	assert(a[] == "abcdef");
640 	a ~= a;
641 	assert(a[] == "abcdefabcdef");
642 }
643 
644 version(emsi_containers_unittest) unittest
645 {
646 	enum initialValue = 0x69FF5705DAD1AB6CUL;
647 	enum payloadValue = 0x495343303356D18CUL;
648 
649 	static struct S
650 	{
651 		ulong value = initialValue;
652 		@nogc:
653 		@disable this();
654 		this(ulong value) { this.value = value; }
655 		~this() { assert(value == initialValue || value == payloadValue); }
656 	}
657 
658 	auto s = S(payloadValue);
659 
660 	DynamicArray!S arr;
661 	arr.insertBack(s);
662 	arr ~= [s];
663 }
664 
665 version(emsi_containers_unittest) @nogc unittest
666 {
667 	DynamicArray!int a;
668 	a.resize(5, 42);
669 	assert(a.length == 5);
670 	assert(a[2] == 42);
671 	a.resize(3, 17);
672 	assert(a.length == 3);
673 	assert(a[2] == 42);
674 
675 	struct Counter
676 	{
677 		@nogc:
678 		static int count;
679 		@disable this();
680 		this(int) { count++; }
681 		this(this) { count++; }
682 		~this() { count--; }
683 	}
684 
685 	DynamicArray!Counter b;
686 	assert(Counter.count == 0);
687 	static assert(!is(typeof(b.resize(5))));
688 	b.resize(5, Counter(0));
689 	assert(Counter.count == 5);
690 	b.resize(3, Counter(0));
691 	assert(Counter.count == 3);
692 }
693 
694 version(emsi_containers_unittest) @nogc unittest
695 {
696 	struct S { int i = 42; @disable this(this); }
697 	DynamicArray!S a;
698 	a.resize(1);
699 	assert(a[0].i == 42);
700 }
701 
702 version(emsi_containers_unittest) unittest
703 {
704 	import std.experimental.allocator.building_blocks.region : Region;
705 	auto region = Region!Mallocator(1024);
706 
707 	auto arr = DynamicArray!(int, Region!(Mallocator)*, true)(&region);
708 	// reserve and insert back call the common form of reallocate
709 	arr.reserve(10);
710 	arr.insertBack(1);
711 	assert(arr[0] == 1);
712 }
713 
714 version(emsi_containers_unittest) unittest
715 {
716 	auto arr = DynamicArray!int();
717 	arr.resize(5);
718 	arr[] = [1, 2, 3, 4, 5];
719 	arr[1 .. 4] = [12, 13, 14];
720 	assert(arr[] == [1, 12, 13, 14, 5]);
721 }